home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 06 - 1990 / 06.07 Jul 90 / Data Exchange ƒ / FixPObj.mod < prev    next >
Encoding:
Text File  |  1989-01-20  |  14.5 KB  |  633 lines  |  [TEXT/MPS ]

  1. MODULE FixPObj;
  2. (*
  3.    Go through an object code file and change dictionary occurrences of one 
  4. string to another string.  The default behavior is to change "QUICKDRAW" to
  5. "QuickDraw__Globals" which makes code generated by the Pascal compiler 
  6. compatible with code generated by the Modula compiler.
  7.    This utility only understands MPW object file format version 1.  It will
  8. print a warning if it encounters a later format version.
  9.  
  10.    Arguments:
  11.               --Input file name is required.
  12.                  --Output file name may be specified with '-o' option, otherwise
  13.                    a default output name of FixP.o will be used. The output file
  14.                     name must be different from the input file name.
  15.                  --Strings for substitution may be specified with the '-s' option.
  16.                     i.e., "-s QUICKDRAW=QuickDraw__Globals" would specify the default
  17.                     behavior.
  18.                   
  19.       Written by John N. Calley   9/20/88
  20.         
  21. *)
  22. FROM Diagnostic IMPORT
  23.    (*procs*) WriteString, WriteCard, WriteLn, WriteLongInt, WriteInt;
  24. FROM FileManager IMPORT
  25.    (*types*) FInfo, 
  26.     (*procs*) GetFInfo, SetFInfo;
  27. FROM IntEnv IMPORT
  28.    (*vars *) ArgC, ArgV, Exit;
  29. FROM IntEnvIO IMPORT
  30.    (*const*) InputFD, OutputFD, RDONLY, WRONLY, CREAT, TRUNC,
  31.     (*procs*) ErrNo, open, read, write, close;
  32. FROM MacTypes IMPORT
  33.    (*types*) Str255, StringHandle, OSErr;
  34. FROM MemoryManager IMPORT
  35.    (*procs*) NewHandle, DisposHandle, HLock, HUnlock;
  36. FROM Strings IMPORT
  37.    (*procs*) Length, MakePascalString, Copy, Pos;
  38. FROM SYSTEM IMPORT
  39.    (*types*) ADDRESS,
  40.    (*procs*) VAL, SHIFT, ADR, LONG;
  41. FROM Utilities IMPORT
  42.    (*procs*) Munger;
  43.     
  44. CONST
  45.    defaultOutFile = "FixP.o";
  46.    pp = FALSE;   (* print progress information *)
  47.     
  48. VAR
  49.     inFileName,
  50.     outFileName,
  51.     inString,
  52.     outString
  53.        :Str255;
  54.         
  55.     inFile,       (* File IDs for input and output files *)
  56.     outFile,
  57.     status        (* Status of last read or write operation *)
  58.        :LONGINT;
  59.         
  60.         
  61. PROCEDURE PrintUsage();
  62. (*
  63.    Print usage statement.
  64. *)
  65. BEGIN
  66.    WriteString ("# ");
  67.    WriteString (ArgV^[0]^);
  68.    WriteString (": Bad option or unable to open file.");
  69.     WriteLn();
  70.    WriteString ("# Usage: ");
  71.    WriteString (ArgV^[0]^);
  72.    WriteString (" [-s oldString=newString] [-o outFileName] inFileName");
  73.     WriteLn();
  74. END PrintUsage;
  75.  
  76. PROCEDURE SetOptions():BOOLEAN;
  77. (*
  78.    Set up input file name, optional output file name and optional string substitutions.  Return
  79. TRUE if all options are interpretable, FALSE if there is an unrecognizable option or if no
  80. input file name is given.
  81. *)
  82. VAR
  83.     i, j
  84.        :INTEGER;
  85.    tempLength
  86.       :INTEGER;
  87.     optionsOK
  88.        :BOOLEAN;
  89.     equalPos        (* Position of '=' in substitution option *)
  90.        :INTEGER;
  91. BEGIN
  92. (* Set defaults *)
  93.    inFileName := "";
  94.     outFileName := defaultOutFile;
  95.     inString := "QUICKDRAW";   (* default substitutions *)
  96.     outString := "QuickDraw__Globals";
  97.    optionsOK := TRUE;
  98.     i := 1;
  99.     WHILE i < ArgC DO
  100.       IF ArgV^[i]^[0] = '-' THEN
  101.             IF CAP(ArgV^[i]^[1]) = "o" THEN
  102.                 INC(i);  (* next argument should be output file name *)
  103.                 outFileName := VAL(Str255, ArgV^[i]^);
  104.             ELSIF CAP(ArgV^[i]^[1]) = "s" THEN
  105.                INC(i);  (* next argument shoud be set of substitution strings *)
  106.                 equalPos := Pos ("=", ArgV^[i]^);
  107.                 IF equalPos = -1 THEN
  108.                    optionsOK := FALSE;
  109.                 ELSE
  110.                    Copy (ArgV^[i]^, 0, equalPos, inString);
  111.                     Copy (ArgV^[i]^, equalPos + 1, 
  112.                                      VAL(INTEGER, Length(ArgV^[i]^)) - equalPos,
  113.                                      outString);
  114.                 END; (*IF*)
  115.             ELSE (* Unknown '-' option *)
  116.                 optionsOK := FALSE;
  117.             END; (*IF*)
  118.         ELSE (* We assume it is the input file name *)
  119.            inFileName := VAL(Str255, ArgV^[i]^);
  120.        END; (*IF*)
  121.         INC(i);
  122.     END; (*WHILE*)
  123.     RETURN (optionsOK);
  124. END SetOptions;
  125.  
  126. PROCEDURE OpenFiles():BOOLEAN;
  127. (*
  128.    Open the files indicated by <inFileName> and <outFileName>. Return TRUE if the operations
  129. are successful, FALSE otherwise.
  130. *)
  131. VAR
  132.    success
  133.        :BOOLEAN;
  134.  
  135. BEGIN
  136.    success := TRUE;
  137.     IF Length(inFileName) = 0 THEN
  138.        success := FALSE;
  139.     ELSE
  140.        inFile := open (inFileName, RDONLY);
  141.         IF ErrNo() <> 0D THEN
  142.            success := FALSE;
  143.         END; (*IF*)
  144.     END; (*IF*)
  145.    IF Length(outFileName) = 0 THEN
  146.        outFile := OutputFD;   (* Standard output *)
  147.     ELSE
  148.        outFile := open (outFileName, WRONLY + CREAT + TRUNC);
  149.         IF ErrNo() <> 0D THEN
  150.            success := FALSE;
  151.         END; (*IF*)
  152.     END; (*IF*)
  153.     
  154.     RETURN (success);
  155. END OpenFiles;
  156.  
  157.  
  158. PROCEDURE ReadWord (VAR value:CARDINAL);
  159. (*
  160.    Read the next two characters as a binary value and return that value.
  161. *)
  162. BEGIN
  163.    status := read (inFile, ADR(value), 2D);
  164. END ReadWord;
  165.  
  166. PROCEDURE WriteWord (value:CARDINAL);
  167. (*
  168.    Write out the indicated integer as two consecutive bytes.
  169. *)
  170. BEGIN
  171.    status := write (outFile, ADR(value), 2D);
  172. END WriteWord;
  173.  
  174. PROCEDURE WriteByte (value:INTEGER);
  175. (*
  176.    Write out the low byte of the indicated integer.
  177. *)
  178. BEGIN
  179.    status := write (outFile, ADR(value) + 1D, 1D);
  180. END WriteByte;
  181.  
  182. PROCEDURE ReadByte ():CHAR;
  183. (*
  184.    Read in one byte and return it as a character
  185. *)
  186. VAR
  187.     tempChar
  188.        :CHAR;
  189. BEGIN
  190.    status := read (inFile, ADR(tempChar), 1D);
  191.     RETURN (tempChar);
  192. END ReadByte;
  193.  
  194. PROCEDURE Pass (length:CARDINAL);
  195. (*
  196.    Pass through the indicated number of bytes from input to output. 
  197. *)
  198. VAR
  199.    tempStr
  200.        :Str255;
  201.     tempLength
  202.        :CARDINAL;
  203. BEGIN
  204.    WHILE length > 0 DO
  205.        IF length > 256 THEN
  206.            tempLength := 256;
  207.             DEC(length, 256);
  208.         ELSE
  209.            tempLength := length;
  210.             length := 0;
  211.         END; (*IF*)
  212.         status := read (inFile, ADR(tempStr), LONG(tempLength));
  213.         status := write (outFile, ADR(tempStr), LONG(tempLength));
  214.     END; (*WHILE*)
  215. END Pass;
  216.  
  217. PROCEDURE ReadString (VAR string:Str255; VAR length:INTEGER);
  218. (*
  219.    Read the pascal formatted string from the input and return it in <string>. 
  220. <length> is the length of the returned string.
  221. *)
  222. VAR
  223.     inChar
  224.        :CHAR;
  225. BEGIN
  226.    status := read (inFile, ADR(inChar), 1D);
  227.     length := VAL(INTEGER, inChar);
  228.     status := read (inFile, ADR(string), LONG(length));
  229.     string[length] := VAL(CHAR, 0);  (* null terminate the string *)
  230. END ReadString;
  231.  
  232. PROCEDURE WritePString (string:Str255);
  233. (*
  234.    Write out the indicated string in pascal format.
  235. *)
  236. VAR
  237.    tempStr
  238.        :Str255;
  239. BEGIN
  240.    MakePascalString (string, tempStr);
  241.     status := write (outFile, ADR(tempStr), LONG(VAL(INTEGER, tempStr[0]) + 1));
  242. END WritePString;
  243.  
  244. PROCEDURE ProcessFirst();
  245. (*
  246.    Pass a First record through. Print a warning if the version number is later
  247. that the latest we know about (1).
  248. *)
  249. VAR
  250.     version
  251.        :CARDINAL;
  252. BEGIN
  253.    IF pp THEN
  254.         WriteString ("First");
  255.         WriteLn();
  256.     END; (*IF*)
  257.     WriteByte (1);
  258.     Pass (1);
  259.     ReadWord (version);
  260.     WriteWord (version);
  261.     IF version > 1 THEN
  262.        WriteString ("# Warning: Unknown object file format version. ");
  263.         WriteLn();
  264.         WriteString ("# Output may not be correct.");
  265.         WriteLn();
  266.     END; (*IF*)
  267. END ProcessFirst;
  268.  
  269. PROCEDURE ProcessLast();
  270. (*
  271.    Pass a Last record through.
  272. *)
  273. BEGIN
  274.    IF pp THEN
  275.         WriteString ("Last");
  276.         WriteLn();
  277.     END; (*IF*)
  278.     WriteByte (2);
  279.     Pass (1); 
  280. END ProcessLast;
  281.  
  282. PROCEDURE ProcessComment();
  283. (*
  284.    Pass a comment record on through.
  285. *)
  286. VAR
  287.     size
  288.        :CARDINAL;
  289. BEGIN
  290.    IF pp THEN
  291.        WriteString ("Comment record");
  292.         WriteLn();
  293.     END; (*IF*)
  294.     WriteByte (3);
  295.     Pass (1);
  296.     ReadWord (size);
  297.     WriteWord (size);
  298.     Pass (size - 4);
  299. END ProcessComment;
  300.  
  301. PROCEDURE ReadDict (dict:StringHandle; length:LONGINT);
  302. (*
  303.    Read <length> bytes from standard input into the handle <dict>.
  304. *)
  305. BEGIN
  306.    HLock (dict);
  307.     status := read (inFile, dict^, length);
  308.    HUnlock (dict);
  309. END ReadDict;
  310.  
  311. PROCEDURE ModifyDict (dict:StringHandle):BOOLEAN;
  312. (*
  313.    Substitute <outString> for the string <inString> in <dict>.  There will not be more
  314. that one occurrence.
  315.    Return TRUE if a replacement was done, FALSE if no replacement occurred.
  316. *)
  317. VAR
  318.    pInString,   (* <inString> and <outString> are modula strings, we actually need *)
  319.     pOutString   (* to replace pascal format strings. *)
  320.        :Str255;
  321.     result
  322.        :LONGINT;
  323. BEGIN
  324.    MakePascalString (inString, pInString);
  325.     MakePascalString (outString, pOutString);
  326.     result := Munger (dict, 2, ADR(pInString), LONG(VAL(INTEGER, pInString[0]) + 1),
  327.                                ADR(pOutString), LONG(VAL(INTEGER, pOutString[0]) + 1));
  328.     IF result > 0D THEN
  329.        RETURN(TRUE);
  330.     ELSE
  331.        RETURN(FALSE);
  332.     END; (*IF*)
  333. END ModifyDict;
  334.  
  335. PROCEDURE WriteDict (dict:StringHandle; length:LONGINT);
  336. (*
  337.    Write <length> bytes from <dict> to standard output.
  338. *)
  339. BEGIN
  340.    HLock (dict);
  341.     status := write (outFile, dict^, length);
  342.    HUnlock (dict);
  343. END WriteDict;
  344.  
  345. PROCEDURE ProcessDict();
  346. (*
  347.    Process a dictionary record. If the record defines the string <inString> then replace it
  348. with the string <outString> and write out the modified dictionary record.  If it does not
  349. contain <inString> write it out unchanged.
  350.  
  351.    Method:
  352.        Find out what the current length of the record is.
  353.         Allocate a handle that is large enough for the record after the string has been changed.
  354.         Read the record into the handle.
  355.         Use Munger to perform the substitution if any.
  356.         Write the potentially modified record back out.
  357. *)
  358. VAR
  359.    inChar
  360.        :CHAR;
  361.     length      (* length of the dictionary record *)
  362.        :CARDINAL;
  363.     wasOdd      (* TRUE if the original dictionary record had an odd length *)
  364.        :BOOLEAN;
  365.     dict
  366.        :StringHandle;
  367. BEGIN
  368.    IF pp THEN
  369.         WriteString ("Dictionary");
  370.         WriteLn();
  371.     END; (*IF*)
  372.    inChar := ReadByte();     (* This byte should always be 0 *)
  373.     ReadWord (length); (* length of the dictionary record *)
  374.     IF pp THEN
  375.         WriteString ("Dictionary length is ");
  376.         WriteCard (length, 4);
  377.         WriteLn();
  378.     END; (*IF*)
  379.     wasOdd := ODD(length);
  380.     dict := NewHandle (length + Length(outString));
  381.     length := length - 4;  (* Compensate for the fact that we have already read 4 bytes of header*)
  382.     ReadDict (dict, length);   (* Read the dictionary into the <dict> handle *)
  383.     IF ModifyDict (dict) THEN
  384.        IF pp THEN
  385.             WriteString ("Changed: Old length = ");
  386.             WriteCard (length, 4);
  387.         END; (*IF*)
  388.        length := length + Length(outString) - Length(inString);
  389.         IF pp THEN
  390.             WriteString ("  outString = '");
  391.             WriteString (outString);
  392.             WriteString ("'  length = '");
  393.             WriteCard (Length(outString), 4);
  394.             WriteString ("  inString = '");
  395.             WriteString (inString);
  396.             WriteString ("'  length = '");
  397.             WriteCard (Length(inString), 4);
  398.             WriteLn();
  399.             WriteString ("  New Dictionary length = ");
  400.             WriteCard (length, 4);
  401.             WriteLn();
  402.         END; (*IF*)
  403.     END;
  404.     WriteByte (4);
  405.     WriteByte (0);
  406.     WriteWord (length + 4);
  407.     WriteDict (dict, length);
  408.     IF NOT (wasOdd = ODD(length)) THEN
  409.        WriteByte (0);   (* Write a pad record *)
  410.     END; (*IF*)
  411.     DisposHandle (dict);
  412. END ProcessDict;
  413.  
  414. PROCEDURE ProcessPad();
  415. (*
  416.    Acknowledge that a padding record has been read.
  417. *)
  418. BEGIN
  419.    WriteByte (0);
  420.    IF pp THEN
  421.         WriteString ("Pad");
  422.         WriteLn();
  423.     END; (*IF*)
  424. END ProcessPad;
  425.  
  426. PROCEDURE ProcessDataModule();
  427. (*
  428.    Pass a data module record on through.
  429. *)
  430. VAR
  431.    moduleID,
  432.     size
  433.        :CARDINAL;
  434. BEGIN
  435.    ReadWord (moduleID);
  436.     WriteWord (moduleID);
  437.     ReadWord (size);
  438.     WriteWord (size);
  439.     IF pp THEN
  440.         WriteString ("Data Module: ");
  441.         WriteCard (moduleID, 4);
  442.         WriteString ("size is ");
  443.         WriteCard (size, 4);
  444.         WriteLn();
  445.     END; (*IF*)
  446. END ProcessDataModule;
  447.  
  448. PROCEDURE ProcessCodeModule();
  449. (*
  450.    Pass a code module record on through.
  451. *)
  452. VAR
  453.    moduleID,
  454.     segID
  455.        :CARDINAL;
  456. BEGIN
  457.    ReadWord (moduleID);
  458.     WriteWord (moduleID);
  459.     ReadWord (segID);
  460.     WriteWord (segID);
  461.     IF pp THEN
  462.         WriteString ("Code Module: ");
  463.         WriteCard (moduleID, 4);
  464.         WriteString (" seg ID:");
  465.         WriteCard (segID, 4);
  466.         WriteLn();
  467.     END; (*IF*)
  468. END ProcessCodeModule;
  469.  
  470. PROCEDURE ProcessModule();
  471. (*
  472.    Pass a module record on through.
  473. *)
  474. VAR
  475.     inChar
  476.         :CHAR;
  477.     flags
  478.         :INTEGER;
  479.     
  480. BEGIN
  481.    WriteByte (5);
  482.    inChar := ReadByte (); (*flags*)
  483.     flags := VAL(INTEGER, inChar);
  484.     WriteByte (flags);
  485.     IF ODD(flags) THEN
  486.        ProcessDataModule();
  487.     ELSE
  488.        ProcessCodeModule();
  489.     END; (*IF*)
  490. END ProcessModule;
  491.     
  492. PROCEDURE ProcessEntryPoint();
  493. (*
  494.    Pass an entry point record on through.
  495. *)
  496. BEGIN
  497.    WriteByte (6);
  498.     Pass (7);
  499.     IF pp THEN
  500.         WriteString ("Entry Point");
  501.         WriteLn();
  502.     END; (*IF*)
  503. END ProcessEntryPoint;
  504.  
  505. PROCEDURE ProcessSize();
  506. (*
  507.    Pass a size record on through.
  508. *)
  509. BEGIN
  510.    WriteByte (7);
  511.     Pass (5);
  512. END ProcessSize;
  513.  
  514. PROCEDURE ProcessContents();
  515. (*
  516.    Pass a contents record on through.
  517. *)
  518. VAR
  519.     size          (* Size of the contents record *)
  520.        :CARDINAL;
  521. BEGIN
  522.    WriteByte (8);
  523.     Pass (1);       (* flags *)
  524.     ReadWord (size);
  525.     WriteWord (size);
  526.     IF pp THEN
  527.         WriteString ("Contents:   size=");
  528.         WriteCard (size, 4);
  529.         WriteLn();
  530.     END; (*IF*)
  531.     Pass (size - 4);
  532. END ProcessContents;
  533.  
  534. PROCEDURE ProcessReference ();
  535. (*
  536.    Pass a reference record on through.
  537. *)
  538. VAR
  539.     size
  540.        :CARDINAL;
  541. BEGIN
  542.    WriteByte (9);
  543.    IF pp THEN
  544.         WriteString ("Reference Record");
  545.         WriteLn();
  546.     END; (*IF*)
  547.     Pass (1);      (* flags *)
  548.     ReadWord (size);
  549.     WriteWord (size);
  550.     Pass (size - 4);
  551. END ProcessReference;
  552.  
  553. PROCEDURE ProcessCReference ();
  554. (*
  555.    Pass a computed reference record on through.
  556. *)
  557. VAR
  558.     size
  559.        :CARDINAL;
  560. BEGIN
  561.    WriteByte (10);
  562.     IF pp THEN
  563.         WriteString ("Computed Reference Record");
  564.         WriteLn();
  565.     END; (*IF*)
  566.     Pass (1);      (* flags *)
  567.     ReadWord (size);
  568.     WriteWord (size);
  569.     Pass (size - 4);
  570. END ProcessCReference;
  571.  
  572. PROCEDURE Dispatch (inChar:CHAR);
  573. (*
  574.    Decide who should process this and dispatch control to them.
  575. *)
  576. BEGIN
  577.    CASE VAL(INTEGER, inChar) OF
  578.        0 : ProcessPad();        |
  579.        1 : ProcessFirst();      |
  580.         2 : ProcessLast();       |
  581.         3 : ProcessComment();    |
  582.         4 : ProcessDict();       |
  583.         5 : ProcessModule();     |
  584.         6 : ProcessEntryPoint(); |
  585.         7 : ProcessSize();       |
  586.         8 : ProcessContents();   |
  587.         9 : ProcessReference();  |
  588.         10: ProcessCReference(); |
  589.         ELSE
  590.            (* 
  591.                This happens when the byte past the last byte of the file is read.
  592.                Ignore it.
  593.             *)
  594.     END; (*CASE*)
  595. END Dispatch;
  596.  
  597. PROCEDURE SetOutFileType();
  598. (*
  599.    We created a text file, we need to make it into and OBJ file so that
  600. the linker will accept it.
  601. *)
  602.  
  603. VAR
  604.    fInfo
  605.        :FInfo;
  606.     err
  607.        :INTEGER;
  608. BEGIN
  609.     err := GetFInfo (outFileName, 0, fInfo);
  610.     IF err = 0 THEN
  611.         fInfo.fdType := 'OBJ ';
  612.         err := SetFInfo (outFileName, 0, fInfo);
  613.     END; (*IF*)
  614.     IF err <> 0 THEN
  615.         WriteString ("# Problem setting output file type to 'OBJ '");
  616.         WriteLn();
  617.     END; (*IF*)
  618. END SetOutFileType;
  619.  
  620. BEGIN (*Main*)
  621.    IF SetOptions() AND OpenFiles() THEN
  622.         REPEAT
  623.             Dispatch (ReadByte());
  624.         UNTIL status = 0D;
  625.         SetOutFileType(); 
  626.         Exit (0D);
  627.     ELSE
  628.        PrintUsage();
  629.         Exit (1D);
  630.     END; (*IF*)
  631. END FixPObj.
  632. ove it back so that it points at the beginning of the record instead of to the end.  We can then use this pointer to directly set and look at the Pascal data.
  633. Accessing the data from a Modula program presents a somewhat different problem.  Modula, like Pascal, expects global data to be accessed as offsets into a data module.  Unlike Pascal, however, Modula does not read the original source code in order to find a particular piece of data.